#if !SILVERLIGHT
using System;
using System.Net;
using System.Collections.Generic;
using System.Web.Services.Protocols;
using System.Reflection;
using System.Configuration;

using SBPweb.Common.Authorization;

namespace SBPweb.Common.Connectivity
{
	/// <summary>
	/// Summary description for WebServiceFactory.
	/// </summary>

	public static class WebServiceFactory
	{
		#region Private members

		private static CookieContainer mCookieContainer;

		private static Dictionary<Type, SoapHttpClientProtocol> mServices = new Dictionary<Type, SoapHttpClientProtocol>();
		private static Dictionary<Type, bool> mServicesPresences = new Dictionary<Type, bool>();

		private static int timeout = 20000;
		private static bool mCheckAlways = true;
		//private static object lockObject = new Object();

		#endregion

		#region Constructor logic

		#endregion

		#region Property accessors

		/// <summary>
		/// Check always the connection
		/// </summary>
		public static bool CheckAlways
		{
			get
			{
				return mCheckAlways;
			}
			set
			{
				mCheckAlways = value;
			}
		}

		internal static CookieContainer CookieJar
		{
			get
			{
				if (mCookieContainer != null)
					return mCookieContainer;

				return mCookieContainer = new CookieContainer();
			}
		}

		#endregion

		#region Public methods

		/// <summary>
		/// Get service
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <returns>service</returns>
		public static T GetService<T>()
			where T : SoapHttpClientProtocol
		{
			return GetService<T>(null, null, null);
		}

		/// <summary>
		/// Get service
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <param name="configurationSourceSection">configsection</param>
		/// <param name="sectionName">section name</param>
		/// <param name="settingName">settings name</param>
		/// <returns>service</returns>
		public static T GetService<T>(string configurationSourceSection, string sectionName, string settingName)
			where T : SoapHttpClientProtocol
		{
			Type type = typeof(T);
			if (!typeof(SoapHttpClientProtocol).IsAssignableFrom(type))
			{
				throw new System.InvalidCastException("SoapHttpClientProtocol is not assignable from given type!");
			}

			if (mServices.ContainsKey(type))
			{
				return (T)mServices[type];
			}

			SoapHttpClientProtocol service;
			ConstructorInfo ci = type.GetConstructor(new Type[] { });

			service = (SoapHttpClientProtocol)ci.Invoke(null);

			if (!string.IsNullOrEmpty(configurationSourceSection) && !string.IsNullOrEmpty(sectionName) && !string.IsNullOrEmpty(settingName))
			{
				string url = GetWebserviceUrlFromConfigFile(configurationSourceSection, sectionName, settingName);
				service.Url = url;
			}

			service.EnableDecompression = true;
			service.UseDefaultCredentials = true;
			service.CookieContainer = CookieJar;

			mServices.Add(type, service);
			if (!IsServiceAvailable<T>())
			{
				return default(T);
			}

			return (T)service;
		}

		/// <summary>
		/// Is Service available?
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <returns>true or false</returns>
		public static bool IsServiceAvailable<T>()
			where T : SoapHttpClientProtocol
		{
			try
			{
				Type type = typeof(T);
				lock (type)
				{
					if (!mServices.ContainsKey(type))
					{
						if (GetService<T>() == null)
						{
							return false;
						}
					}
					SoapHttpClientProtocol service = mServices[type];
					if (!mServicesPresences.ContainsKey(type))
					{
						mServicesPresences.Add(type, false);
					}
					if (!mServicesPresences[type] || CheckAlways)
					{
						if (string.IsNullOrEmpty(service.Url))
						{
							mServicesPresences.Add(type, false);
							return false;
						}
						try
						{
							if (CheckTurnaround<T>() > TimeSpan.MinValue)
							{
								return mServicesPresences[type] = true;
							}
							else
							{
								WebRequest request = WebRequest.Create(service.Url + "?WSDL");
								request.Credentials = service.Credentials;
								request.Timeout = timeout;
								HttpWebResponse response = (HttpWebResponse)request.GetResponse();
								System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(response.GetResponseStream());
								mServicesPresences[type] = System.Web.Services.Description.ServiceDescription.CanRead(xmlReader);
								xmlReader.Close();
								response.Close();
								return mServicesPresences[type] = true;
							}
						}
						catch (ArgumentNullException)
						{
							return mServicesPresences[type] = false;
						}
						catch (System.Security.SecurityException)
						{
							return mServicesPresences[type] = false;
						}
						catch (NotSupportedException)
						{
							return mServicesPresences[type] = false;
						}
						catch (NotImplementedException)
						{
							return mServicesPresences[type] = false;
						}
						catch (InvalidOperationException)
						{
							return mServicesPresences[type] = false;
						}
					}
				}
				return false;
			}
			catch (ArgumentNullException)
			{
				return false;
			}
			catch (ArgumentException)
			{
				return false;
			}
		}

		/// <summary>
		/// Invalidate service
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		public static void InvalidateService<T>()
			where T : SoapHttpClientProtocol
		{
			Type type = typeof(T);

			if (!mServicesPresences.ContainsKey(type))
			{
				mServicesPresences.Add(type, false);
			}
			mServicesPresences[type] = false;
		}

		/// <summary>
		/// Check turn around time
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <returns>Time</returns>
		public static Nullable<TimeSpan> CheckTurnaround<T>()
			where T : SoapHttpClientProtocol
		{
			try
			{
				Type type = typeof(T);

				MethodInfo mI = type.GetMethod("Ping", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);

				if (mI == null && !mServices.ContainsKey(type))
				{
					return null;
				}

				if (mI == null)
				{
					return TimeSpan.MinValue;
				}

				SoapHttpClientProtocol service = GetService<T>();

				DateTime stamp1 = DateTime.Now;

				object ret = mI.Invoke(service, new object[] { });

				DateTime stamp2 = DateTime.Now;

				type = ret.GetType();
				object result;

				if (type == typeof(String))
				{
					result = ret;
				}
				else
				{
					PropertyInfo pI = type.GetProperty("Result", BindingFlags.Public | BindingFlags.Instance | BindingFlags.Default);
					result = pI.GetValue(ret, new object[] { });
				}

				if (result != null && result.ToString() == "Pong")
				{
					return stamp2.Subtract(stamp1);
				}

				return TimeSpan.MinValue;
			}
			catch (System.Reflection.AmbiguousMatchException)
			{
				return TimeSpan.MinValue;
			}
			catch (ArgumentNullException)
			{
				return TimeSpan.MinValue;
			}
			catch (MethodAccessException)
			{
				return TimeSpan.MinValue;
			}
			catch (InvalidOperationException)
			{
				return TimeSpan.MinValue;
			}
			catch (System.Reflection.TargetParameterCountException)
			{
				return TimeSpan.MinValue;
			}
			catch (System.Reflection.TargetInvocationException)
			{
				return TimeSpan.MinValue;
			}
			catch (System.Reflection.TargetException)
			{
				return TimeSpan.MinValue;
			}
			catch (ArgumentOutOfRangeException)
			{
				return TimeSpan.MinValue;
			}
			catch (ArgumentException)
			{
				return TimeSpan.MinValue;
			}
		}

		#endregion

		#region Private helper methods

		private static string GetWebserviceUrlFromConfigFile(string configurationSourceSection, string sectionName, string settingName)
		{
			string retVal = string.Empty;

			Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection csc = ConfigurationManager.GetSection("externalConfigurationSource") as Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection;
			Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSourceElement fcse = csc.Sources.Get(configurationSourceSection) as Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSourceElement;

			string path = System.IO.Path.GetDirectoryName(fcse.FilePath);
			Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource fcs;
			ExeConfigurationFileMap ecfm;

			if (!string.IsNullOrEmpty(path))
			{
				//AppDomain.CurrentDomain.AppendPrivatePath(path);
				fcs = new Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource(fcse.FilePath);
				ecfm = new ExeConfigurationFileMap();
				ecfm.ExeConfigFilename = fcse.FilePath;
			}
			else
			{
				path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fcse.FilePath);
				fcs = new Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource(path);
				ecfm = new ExeConfigurationFileMap();
				ecfm.ExeConfigFilename = path;
			}


			System.Configuration.Configuration cfg = ConfigurationManager.OpenMappedExeConfiguration(ecfm, ConfigurationUserLevel.None);
			System.Configuration.ConfigurationSectionGroup cfgGroup = cfg.GetSectionGroup("applicationSettings");
			System.Configuration.ConfigurationSection settings = cfgGroup.Sections[sectionName];
			retVal = (settings as System.Configuration.ClientSettingsSection).Settings.Get(settingName).Value.ValueXml.InnerText;

			return retVal;
		}

		#endregion
	}
}
#endif